home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C++ / Applications / Nuntius 1.2 / src / Nuntius / UBinariesCmds.cp < prev    next >
Encoding:
Text File  |  1994-04-13  |  21.4 KB  |  928 lines  |  [TEXT/MPS ]

  1. // Copyright © 1992 Peter Speck, speck@dat.ruc.dk. All rights reserved.
  2. // UBinariesCmds.cp
  3.  
  4. #include "UBinariesCmds.h"
  5. #include "UBinDecoders.h"
  6. #include "UArticleCache.h"
  7. #include "UArticle.h"
  8. #include "UThread.h"
  9. #include "UProgress.h"
  10. #include "UGroupDoc.h"
  11. #include "Tools.h"
  12. #include "FileTools.h"
  13. #include "UPrefsDatabase.h"
  14. #include "ProcessTools.h"
  15. #include "NetAsciiTools.h"
  16. #include "UDynDynArray.h"
  17.  
  18. #include <ErrorGlobals.h>
  19. #include <RsrcGlobals.h>
  20.  
  21. #include <OSUtils.h>
  22. #include <ToolUtils.h>
  23. #include <Folders.h>
  24. #include <Packages.h>
  25. #ifndef __STDIO__
  26. #include <stdio.h>
  27. #endif
  28.  
  29. #pragma segment MyArticle
  30.  
  31. #define qDebugExtract                    qDebug
  32. #define qDebugExtractSorting    qDebug
  33. #define qDebugScanSubject            qDebug
  34.  
  35. #if qDebug
  36. #define DD(x) fprintf(stderr, x)
  37. #else
  38. #define DD(x)
  39. #endif
  40.  
  41. #if qDebugExtract
  42. #define DDE(x) fprintf(stderr, x)
  43. #else
  44. #define DDE(x)
  45. #endif
  46.  
  47.  
  48. const long kBufferSize = 16 * 1024;
  49. const long kProgressScale = 10000;
  50.  
  51. TProcessArticleLinesCommand::TProcessArticleLinesCommand()
  52. {
  53. }
  54.  
  55. pascal void TProcessArticleLinesCommand::Initialize()
  56. {
  57.     inherited::Initialize();
  58.     fIDList = nil;
  59.     fSubjectList = nil;
  60.     fGroupDotName = "";
  61.     fFile = nil;
  62.     fBufferH = nil;
  63.     fBytesInBuffer = 0;
  64.     fFileName = "";
  65.     fPBP = nil;
  66. }
  67.  
  68. void TProcessArticleLinesCommand::IProcessArticleLinesCommand(CommandNumber itsCommandNumber,
  69.                                                     TGroupDoc *doc, TLongintList *idList,
  70.                                                     Boolean processHeaders)
  71. {
  72.     inherited::ICommand(itsCommandNumber, nil, false, false, nil);
  73.     CStr255 s;
  74.     doc->GetGroupDotName(s);
  75.     fGroupDotName = s;
  76.     FailInfo fi;
  77.     if (fi.Try())
  78.     {
  79.         fIDList = idList;
  80. #if qDebug
  81.         if (!IsObject(fIDList))
  82.             ProgramBreak("fIDList is not object!");
  83. #endif    
  84.         fSubjectList = NewString255Array();
  85.         for (ArrayIndex index = 1; index <= fIDList->fSize; ++index)
  86.         {
  87.             HandleOffsetLength hol;
  88.             CStr255 s;
  89.             if (doc->GetSubject(fIDList->At(index), hol))
  90.                 CopyHolToCStr255(hol, s);
  91.             else
  92.                 s = "?";
  93.             fSubjectList->Append(s);
  94.         }
  95.         fProcessHeaders = processHeaders;
  96.         fBufferH = NewPermHandle(kBufferSize);
  97.         fPBP = (ParamBlockRec*)NewPermPtr(sizeof(ParamBlockRec));        
  98.         fi.Success();
  99.     }
  100.     else // fail
  101.     {
  102.         delete this;
  103.         fi.ReSignal();
  104.     }
  105. }
  106.  
  107. pascal void TProcessArticleLinesCommand::Free()
  108. {
  109.     fBufferH = DisposeIfHandle(fBufferH);
  110.     FreeIfObject(fFile); fFile = nil;
  111.     FreeIfObject(fIDList); fIDList = nil;
  112.     DisposeIfPtr(Ptr(fPBP)); fPBP = nil;
  113.     delete fSubjectList; fSubjectList = nil;
  114.     inherited::Free();
  115. }
  116.  
  117. //---------------------------------- OUTPUT FILE --------------------------------------
  118. void TProcessArticleLinesCommand::OpenFile()
  119. {
  120.     if (FileExist(fFile))
  121.         FailOSErr(fFile->DeleteFile()); // zap it
  122.     FailOSErr(fFile->CreateFile());
  123.     FailOSErr(fFile->OpenFile());
  124. }
  125.  
  126. void TProcessArticleLinesCommand::CloseFile()
  127. {
  128.     fFile->CloseFile();
  129. }
  130.  
  131. //---------------------------------- OUTPUT FILE BUFFER --------------------------------------
  132. void TProcessArticleLinesCommand::FlushBuffer()
  133. {
  134.     HLock(fBufferH);
  135.     WriteASyncToFile(*fPBP, fFile, *fBufferH, fBytesInBuffer);
  136.     HUnlock(fBufferH);
  137.     fBytesInBuffer = 0;
  138. }
  139.  
  140. void TProcessArticleLinesCommand::WriteBytes(const char *p, long noBytes)
  141. {
  142.     while (true)
  143.     {
  144.         long subBytes = Min(noBytes, kBufferSize - fBytesInBuffer);
  145.         BytesMove(p, *fBufferH + fBytesInBuffer, subBytes);
  146.         fBytesInBuffer += subBytes;
  147.         p += subBytes;
  148.         noBytes -= subBytes;
  149.         if (!noBytes)
  150.             break;
  151.         FlushBuffer();
  152.     }
  153. }
  154.  
  155. void TProcessArticleLinesCommand::WriteChar(char ch)
  156. {
  157.     WriteBytes(&ch, 1);
  158. }
  159.  
  160. void TProcessArticleLinesCommand::WriteString(const CStr255 &txt)
  161. {
  162.     if (txt.Length())
  163.     {
  164.         const unsigned char *p = (const unsigned char*)&txt;
  165.         WriteBytes((const char*)p + 1, txt.Length());
  166.     }
  167. }
  168.  
  169. void TProcessArticleLinesCommand::WriteLine(const CStr255 &text)
  170. {
  171.     WriteString(text);
  172.     WriteChar(13);
  173. }
  174.  
  175. //---------------------------------- ARTICLE PROCESSING --------------------------------------
  176. pascal void TProcessArticleLinesCommand::DoIt()
  177. {
  178.     if (fIDList->GetSize() < 1)
  179.     {
  180. #if qDebug
  181.         fprintf(stderr, "TProcessArticleLinesCommand::DoIt, did not get any articles to save!!\n");
  182. #endif
  183.         return; // nothing to save
  184.     }
  185.     FailInfo fi;
  186.     if (fi.Try())
  187.     {
  188.         OSType signature;
  189.         if (gPrefs->PrefExists('EDsi'))
  190.             signature = gPrefs->GetSignaturePrefs('EDsi');
  191.         else
  192.             signature = 'ttxt'; // TeachText
  193.         fFile = NewFile('TEXT', signature, 
  194.                                         kUsesDataFork, noResourceFork, !kDataOpen, !kRsrcOpen);
  195.         AskFileName();
  196.         fFile->SetPermissions(fsRdWrPerm, fsRdWrPerm);
  197.  
  198.         SetupProgressTexts();
  199.         gCurProgress->SetStandardProgressType();
  200.         gCurProgress->SetWorkToDo(2 * fIDList->GetSize() * kProgressScale);
  201.         gCurProgress->StartProgress(true);
  202.  
  203.         OpenFile();
  204.         DoPreProcessing();
  205.         ProcessAllArticles();
  206.         FlushBuffer();
  207.         DoPostProcessing();
  208.         CloseFile();
  209.         gCurProgress->WorkDone();
  210.         fi.Success();
  211.     }
  212.     else // fail
  213.     {
  214.         if (fFile)
  215.             fFile->DeleteFile();
  216.         gCurProgress->WorkDone();
  217.         fi.ReSignal();
  218.     }
  219. }
  220.  
  221. void TProcessArticleLinesCommand::SetupProgressTexts()
  222. {
  223.     SubClassResponsibility();
  224. }
  225.  
  226. void TProcessArticleLinesCommand::MakeDefaultFilename(CStr255 &filename)
  227. {
  228.     fSubjectList->GetStringAt(1, filename);
  229.     CStr255 part;
  230.     MyGetIndString(part, kSubjectPartHeader);
  231.     short pos = filename.Pos(part);
  232.     if (pos > 1 && pos < filename.Length())
  233.         filename.Delete(pos, filename.Length() - pos + 1);
  234.     CheckFilenameSanity(filename);
  235. }
  236.  
  237. void TProcessArticleLinesCommand::AskFileName()
  238. {
  239.     CStr255 defaultFilename, prompt;
  240.     MakeDefaultFilename(defaultFilename);
  241.     GetAskFileNamePromt(prompt);
  242.     FSSpec spec;
  243.     ::AskFileName(prompt, defaultFilename, spec);
  244.     fFile->Specify(spec);
  245.     fFileName = spec.name;
  246. }
  247.  
  248. void TProcessArticleLinesCommand::GetAskFileNamePromt(CStr255 & /* prompt */)
  249. {
  250.     SubClassResponsibility();
  251. }
  252.  
  253. void TProcessArticleLinesCommand::ProcessAllArticles()
  254. {
  255.     long work = 0;
  256.     for (ArrayIndex index = 1; index <= fIDList->GetSize(); index++)
  257.     {
  258. #if qDebugExtract
  259.         fprintf(stderr, "Processes article %ld, id = %ld\n", index, fIDList->At(index));
  260. #endif
  261.         gCurProgress->SetWorkDone(work);
  262.         ProcessOneArticle(fIDList->At(index), work + kProgressScale, work + 2 * kProgressScale);
  263.         work += 2 * kProgressScale;
  264.     }
  265. }
  266.  
  267. void TProcessArticleLinesCommand::ProcessOneArticle(long id, long work1, long work2)
  268. {
  269.     TArticle *article = nil;
  270.     VOLATILE(article);
  271.     FailInfo fi;
  272.     if (fi.Try())
  273.     {
  274.         SetArticleProgressText(id);
  275.         article = gArticleCache->GetArticle(fGroupDotName, id);
  276.         gCurProgress->SetWorkDone(work1);
  277.         gCurThread->CheckYield();
  278.         DoPreProcessArticle(article);
  279.         gCurThread->CheckYield();
  280.         ProcessArticleLines(article, work1, work2);
  281.         gCurProgress->SetWorkDone(work2);
  282.         gCurThread->CheckYield();
  283.         DoPostProcessArticle(article);
  284.         gCurThread->CheckYield();
  285.         gArticleCache->ReturnArticle(article); article = nil;
  286.         fi.Success();
  287.     }
  288.     else // fail
  289.     {
  290.         gArticleCache->ReturnArticle(article); article = nil;
  291.         fi.ReSignal();
  292.     }
  293. }
  294.  
  295. void TProcessArticleLinesCommand::ProcessLine(TArticle * /* article */, CStr255 & /* text */ )
  296. {
  297. }
  298.  
  299. void TProcessArticleLinesCommand::SetArticleProgressText(long id)
  300. {
  301.     CStr255 s;
  302.     GetSubject(id, s);
  303.     gCurProgress->SetText(s);
  304. }
  305.  
  306. void TProcessArticleLinesCommand::GetSubject(long id, CStr255 &s)
  307. {
  308.     ArrayIndex index = fIDList->GetEqualItemNo(id);
  309.     if (index != kEmptyIndex)
  310.         fSubjectList->GetStringAt(index, s);
  311.     else
  312.         s = "?";
  313. }
  314.  
  315. void TProcessArticleLinesCommand::ProcessArticleLines(TArticle *article, long work1, long work2)
  316. {
  317.     if (fNumArticleLines <= 0)
  318.         return;
  319.     ArrayIndex noLines, bodyStartLineNo;
  320.     article->GetLineInfo(noLines, bodyStartLineNo);
  321.     ArrayIndex line = fProcessHeaders ? 1 : bodyStartLineNo;
  322.     while (line <= noLines)
  323.     {
  324.         CStr255 text;
  325.         article->GetLine(line, text);
  326.         ProcessLine(article, text);
  327.         fLineNo++;
  328.         long newWork = MinMax(work1, (fLineNo * kProgressScale) / fNumArticleLines + work1, work2);
  329.         gCurProgress->SetWorkDone(newWork);
  330.         gCurThread->CheckYield();
  331.         line++;
  332.     }
  333. }
  334.  
  335. void TProcessArticleLinesCommand::DoPreProcessArticle(TArticle *article)
  336. {
  337.     long noLines, bodyStartLineNo;
  338.     article->GetLineInfo(noLines, bodyStartLineNo);
  339.     if (!fProcessHeaders)
  340.         noLines -= bodyStartLineNo;
  341.     fNumArticleLines = noLines;
  342.     fLineNo = 0;
  343. }
  344.  
  345. void TProcessArticleLinesCommand::DoPostProcessArticle(TArticle * /* article */)
  346. {
  347. }
  348.  
  349. void TProcessArticleLinesCommand::DoPreProcessing()
  350. {
  351. }
  352.  
  353. void TProcessArticleLinesCommand::DoPostProcessing()
  354. {
  355. }
  356.  
  357. //********************************************************************************
  358. TSaveArticlesCommand::TSaveArticlesCommand()
  359. {
  360. }
  361.  
  362. pascal void TSaveArticlesCommand::Initialize()
  363. {
  364.     inherited::Initialize();
  365.     fNumMissingArticles = 0;
  366. }
  367.  
  368. void TSaveArticlesCommand::ISaveArticlesCommand(CommandNumber itsCommandNumber,
  369.                                                     TGroupDoc *doc, TLongintList *idList)
  370. {
  371. //    Boolean processHeaders = gPrefs->GetBooleanPrefs('Head');
  372.     Boolean processHeaders = true;
  373.     inherited::IProcessArticleLinesCommand(itsCommandNumber, doc, idList, processHeaders);
  374. }
  375.  
  376. pascal void TSaveArticlesCommand::Free()
  377. {
  378.     inherited::Free();
  379. }
  380.  
  381. pascal void TSaveArticlesCommand::DoIt()
  382. {
  383.     FailInfo fi;
  384.     if (fi.Try())
  385.     {
  386.         inherited::DoIt();
  387.         fi.Success();
  388.     }
  389.     else // fail
  390.     {
  391.         FailNewMessage(fi.error, fi.message, messageCannotSaveFiles);
  392.     }
  393. }
  394.  
  395. void TSaveArticlesCommand::SetupProgressTexts()
  396. {
  397.     gCurProgress->SetTitle(kSaveProgressTitle);
  398.     gCurProgress->SetText("");
  399. }
  400.  
  401. void TSaveArticlesCommand::GetAskFileNamePromt(CStr255 &prompt)
  402. {
  403.     MyGetIndString(prompt, kSaveArticlesPrompt);
  404. }
  405.  
  406. void TSaveArticlesCommand::ProcessOneArticle(long id, long work1, long work2)
  407. {
  408.     FailInfo fi;
  409.     if (fi.Try())
  410.     {
  411.         inherited::ProcessOneArticle(id, work1, work2);
  412.         fi.Success();
  413.     }
  414.     else // fail
  415.     {
  416.         if (fi.error == errNoSuchArticle)
  417.             fNumMissingArticles++;
  418.         else
  419.             fi.ReSignal();
  420.     }
  421. }
  422.  
  423. void TSaveArticlesCommand::ProcessLine(TArticle *article, CStr255 &text)
  424. {
  425.     if (article->ContainsJapaneseEncoding() == false)
  426.         TranslateCStr255(text, gNetAscii2Mac);
  427.     WriteLine(text);
  428. }
  429.  
  430. void TSaveArticlesCommand::DoPreProcessArticle(TArticle *article)
  431. {
  432.     inherited::DoPreProcessArticle(article);
  433.     if (fProcessHeaders)
  434.         return;
  435.     CStr255 text, s, email;
  436.  
  437. // subject
  438.     article->GetHeader("Subject", text);
  439.     MyGetIndString(s, kPreSubject);
  440.     text.Insert(s, 1);
  441.     MyGetIndString(s, kPostSubject);
  442.     text += s;
  443.     WriteLine(text);
  444.  
  445.     article->GetHeader("From", text);
  446.     Boolean hadRealName = GetPrintableAuthorName(text, s, email);
  447.     MyGetIndString(text, kPreFrom);
  448.     text += s;
  449.     if (hadRealName)
  450.     {
  451.         MyGetIndString(s, kArticleReal2EmailSeparator);
  452.         text += s;
  453.         text += email;
  454.     }
  455.     MyGetIndString(s, kPostFrom);
  456.     text += s;
  457.     WriteLine(text);
  458.  
  459. // Date
  460.     MyGetIndString(text, kPreDate);
  461.     article->GetHeader("Date", s);
  462.     text += s;
  463.     MyGetIndString(s, kPostDate);
  464.     text += s;
  465.     WriteLine(text);
  466.  
  467. // separator
  468.     WriteLine("");
  469. }
  470.  
  471. void TSaveArticlesCommand::DoPostProcessArticle(TArticle *article)
  472. {
  473.     if (fNumMissingArticles)
  474.     {
  475.         if (fIDList->GetSize() == 1)
  476.             FailOSErr(errNoSuchArticle);
  477.         if (fNumMissingArticles > 1)
  478.         {
  479.             CStr255 s;
  480.             NumToString(fNumMissingArticles, s);
  481.             ParamText(s, gEmptyString, gEmptyString, gEmptyString);
  482.             StdAlert(phMissingSaveArticleMany);
  483.         }
  484.         else
  485.             StdAlert(phMissingSaveArticle);
  486.     }
  487.     CStr255 text;
  488.     MyGetIndString(text, kSaveArticleSeparator);
  489.     WriteLine(text);
  490.     inherited::DoPostProcessArticle(article);
  491. }
  492.  
  493. /////////////////////////////////////////////////////////////////////////////////
  494. TNewBinCommand::TNewBinCommand()
  495. {
  496. }
  497.  
  498. pascal void TNewBinCommand::Initialize()
  499. {
  500.     inherited::Initialize();
  501.     fSecretDecoderRing = nil;
  502.     fExtractorNeeded = false;
  503. }
  504.  
  505. void TNewBinCommand::INewBinCommand(CommandNumber itsCommandNumber,
  506.                                                     TGroupDoc *doc, TLongintList *idList)
  507. {
  508.     inherited::IProcessArticleLinesCommand(itsCommandNumber, doc, idList, false);
  509. }
  510.  
  511. pascal void TNewBinCommand::Free()
  512. {
  513.     FreeIfPtrObject(fSecretDecoderRing); fSecretDecoderRing = nil;
  514.     inherited::Free();
  515. }
  516.  
  517. pascal void TNewBinCommand::DoIt()
  518. {
  519.     FailInfo fi;
  520.     if (fi.Try())
  521.     {
  522.         inherited::DoIt();
  523.         fi.Success();
  524.     }
  525.     else // fail
  526.     {
  527.         if (fSecretDecoderRing)
  528.             fSecretDecoderRing->Abort();
  529.         FailNewMessage(fi.error, fi.message, messageCannotExtractBinaries);
  530.     }
  531. }
  532.  
  533. void TNewBinCommand::AskFileName()
  534. {
  535. }
  536.  
  537. void TNewBinCommand::GetAskFileNamePromt(CStr255 &prompt)
  538. {
  539.     MyGetIndString(prompt, kSaveBinariesPrompt);
  540. }
  541.  
  542. void TNewBinCommand::ProcessLine(TArticle * /* article */, CStr255 &text)
  543. {
  544.     CStr255 s;
  545.     if (fSecretDecoderRing)
  546.     {
  547.         fSecretDecoderRing->DecodeLine(text);
  548.         return;
  549.     }
  550.     if (!text.Length())
  551.         return;
  552.     unsigned char len = text.Length(); // easier to read
  553.     char *p = (char*)&text[1];
  554.     if (strncmp(p, "(This file must be converted with BinHex 4.0", 40) == 0)
  555.     {
  556.         DDE("Found '(This file must…' -> kBinHexWaitForStartColon\n");
  557.         PBinHexDecoder *bhd = new PBinHexDecoder();
  558.         bhd->IBinHexDecoder(fFile);
  559.         fSecretDecoderRing = bhd;
  560.         fExtractorNeeded = true;
  561.         fSecretDecoderRing->DecodeLine(text);
  562.     }
  563.     else if (strncmp(p, "begin ", 6) == 0 && len < 100)
  564.     {
  565.         text += 32; // space to ease parsing
  566.         char *cp = p + 6;
  567.         while (*cp > 32)
  568.         {
  569.             if (*cp < '0' || *cp >= '8')
  570.             {
  571.                 DD("Found 'begin ', but number was not octal, line is ignored\n");
  572.                 return; // not octal
  573.             }
  574.             ++cp;
  575.         }
  576.         long mode;
  577.         char buffer[200];
  578.         if (sscanf(p, "begin %ld %s", &mode, &buffer) != 2)
  579.         {
  580.             DD("Could not sscanf the uuencode filename, line is ignored\n");
  581.             return;
  582.         }
  583. //        if (1 || gPrefs->GetBooleanPrefs('UUna'))
  584. //        {
  585.             s = buffer;
  586.             CheckFilenameSanity(s);
  587.             FSSpec spec;
  588.             gPrefs->GetSilentDirAliasPrefs('FBin', spec);
  589.             CopyCString2String(s, spec.name);
  590.             MakeFilenameUnique(spec);
  591.             s = spec.name;
  592.             if (gPrefs->GetBooleanPrefs('BiAs'))
  593.             {
  594.                 CStr255 prompt;
  595.                 GetAskFileNamePromt(prompt);
  596.                 ::AskFileName(prompt, s, spec);
  597.             }
  598. //        }
  599.         fFileName = s;
  600.         fFile->Specify(spec);
  601.         FailOSErr(fFile->CreateDataFork());
  602.         PUUDecoder *uud = new PUUDecoder();
  603.         uud->IUUDecoder(fFile);
  604.         fSecretDecoderRing = uud;
  605.         uud->PrepareUU();
  606.         s = fFileName;
  607.         if (s.Length() >= 5)
  608.             s.Delete(1, s.Length() - 4);
  609.         UprString(s, false);
  610.         if (s == ".GIF")
  611.         {
  612.             DDE("File was detected as GIF -> changing file type & creator\n");
  613.             ChangeFileTypeAndCreator(fFile, 'GCon', 'GIFf');
  614.         }
  615.         else if (s == ".JPG")
  616.         {
  617.             DDE("File was detected as JPEG -> changing file type & creator\n");
  618.             ChangeFileTypeAndCreator(fFile, 'JVWR', 'JFIF');
  619.         }
  620.         else
  621.         {
  622. #if qDebugExtract
  623.             fprintf(stderr, "Unknown file format: file saved as 'TEXT'\n");
  624.             fprintf(stderr, "- filename = '%s'\n", (char*)fFileName);
  625. #endif
  626.         }
  627.     }
  628. }
  629.  
  630. OSErr TNewBinCommand::ScanSubject(long id, short &part, short &noParts)
  631. {
  632.     // just _why_ are no one using a "Part x of y" header line???
  633.     CStr255 s;
  634.     GetSubject(id, s);
  635. #if qDebugScanSubject
  636.     fprintf(stderr, "ScanSubject of '%s'\n", (char*)s);
  637.     fprintf(stderr, "                ");
  638.     for (short i = 1; i <= s.Length(); ++i)
  639.         fprintf(stderr, "%ld", long(i)%10);
  640.     fprintf(stderr, "\n");
  641. #endif
  642.     if (s.Length() < 3)
  643.     {
  644. #if qDebugScanSubject
  645.     fprintf(stderr, "  too short -> errBinHexBadSubject\n");
  646. #endif
  647.         return errBinHexBadSubject;
  648.     }
  649.     if ( (s[1] == 'R' || s[1] == 'r')
  650.     &&   (s[2] == 'e' || s[2] == 'E')
  651.     &&   (s[3] == ':'))
  652.     {
  653. #if qDebugScanSubject
  654.                 fprintf(stderr, "  starts with 'Re:' -> errBinHexBadSubject\n");
  655. #endif
  656.                 return errBinHexBadSubject;
  657.     }
  658.     short index = s.Length();
  659. TryWithNextNumber:
  660.     while (index && (s[index] < '0' || s[index] > '9')) 
  661.         --index;
  662.     if (index == 0)
  663.     {
  664. #if qDebugScanSubject
  665.         fprintf(stderr, "  couldn't find number -> errBinHexBadSubject\n");
  666. #endif
  667.         return errBinHexBadSubject;
  668.     }
  669. #if qDebugScanSubject
  670.     fprintf(stderr, "  found number end-at %ld\n", long(index));
  671. #endif
  672.     short faktor = 1;
  673.     noParts = 0;
  674.     while (index && s[index] >= '0' && s[index] <= '9')
  675.     {
  676.         noParts += faktor * (s[index] - '0');
  677.         faktor *= 10;
  678.         --index;
  679.     }
  680. #if qDebugScanSubject
  681.     fprintf(stderr, "  got number == noParts == %ld, prev-index = %ld\n", long(noParts), long(index));
  682. #endif
  683.     while (index && s[index] <= 32)
  684.         --index;
  685. #if qDebugScanSubject
  686.     fprintf(stderr, "  prev spaces, index = %ld\n", long(index));
  687. #endif
  688.     if (s[index - 1] == 'o' && s[index] == 'f')
  689.         index -= 2;
  690.     else switch (s[index])
  691.     {
  692.         case '/':
  693.         case '|':
  694.         case ':':
  695.         case '-':
  696.             --index;  // good separator
  697.             break;
  698.         
  699.         case '*':
  700.         case 'x':
  701.         case 'X':        // not part stuff, but resolution etc
  702. #if qDebugScanSubject
  703.             fprintf(stderr, "  got resolution char at index = %ld !> try again\n", long(index));
  704. #endif
  705.             goto TryWithNextNumber;
  706.     
  707.         default:
  708. #if qDebugScanSubject
  709.             fprintf(stderr, "  bad separator at index %ld !> try again\n", long(index));
  710. #endif
  711.             goto TryWithNextNumber; // bad char, cannot be part stuff
  712.     }
  713. #if qDebugScanSubject
  714.     fprintf(stderr, "  prev separator, index = %ld\n", long(index));
  715. #endif
  716.     while (index && s[index] <= 32)
  717.         --index;
  718. #if qDebugScanSubject
  719.     fprintf(stderr, "  prev spaces, index = %ld\n", long(index));
  720. #endif
  721.     if (index <= 0)
  722.     {
  723. #if qDebugScanSubject
  724.         fprintf(stderr, "  no more chars left in string -> errBinHexBadSubject\n");
  725. #endif
  726.         return errBinHexBadSubject;
  727.     }
  728.     if (s[index] < '0' || s[index] > '9')
  729.     {
  730. #if qDebugScanSubject
  731.         fprintf(stderr, "  char at %ld is not digit !> try again\n", long(index));
  732. #endif
  733.         goto TryWithNextNumber;
  734.     }
  735.     faktor = 1;
  736.     part = 0;
  737.     while (index && s[index] >= '0' && s[index] <= '9')
  738.     {
  739.         part += faktor * (s[index] - '0');
  740.         faktor *= 10;
  741.         --index;
  742.     }
  743. #if qDebugScanSubject
  744.     fprintf(stderr, "  got number == part-num == %ld, prev-index = %ld -> OK\n", long(noParts), long(index));
  745. #endif
  746.     return noErr;
  747. }
  748.  
  749. void TNewBinCommand::DoTheSorting()
  750. {
  751.     TSortedLongintList *partList = nil;
  752.     VOLATILE(partList);
  753.     TLongintList *sortedIDList = nil;
  754.     VOLATILE(sortedIDList);
  755.     FailInfo fi;
  756.     if (fi.Try())
  757.     {
  758.         TSortedLongintList *sllist = new TSortedLongintList();
  759.         sllist->ISortedLongintList();
  760.         partList = sllist;
  761.         
  762.         TLongintList *llist = new TLongintList();
  763.         llist->ILongintList();
  764.         sortedIDList = llist;
  765.         
  766.         // extract the 'part x of y', and check y for consistency
  767.         short noParts = 0;
  768.         short i;
  769.         for (i = 1; i <= fIDList->GetSize(); i++)
  770.         {
  771.             long id = fIDList->At(i);
  772.             short thisPart, thisNoParts;
  773.             OSErr err = ScanSubject(id, thisPart, thisNoParts);
  774. #if qDebugExtractSorting
  775.             fprintf(stderr, "Scan: err = %ld, thisPart = %ld, thisNoParts = %ld\n", long(err), long(thisPart), long(thisNoParts));
  776. #endif
  777.             if (err)
  778.                 continue;
  779.             if (thisPart == 0 && thisNoParts)
  780.                 continue; // skip
  781.             if (!noParts)
  782.             {
  783.                 noParts = thisNoParts; // first one
  784.                 if (noParts < 1 || noParts > 100) // no one sends a +100 part file (until MIME!)
  785.                     FailOSErr(errBinHexMultiSubjectError);
  786.                 if (noParts > fIDList->GetSize())
  787.                     FailOSErr(errBinariesMissingPart);
  788.             }
  789.             if (    thisPart < 1 || 
  790.                         thisNoParts != noParts ||
  791.                         thisPart > noParts)
  792.                         // minimum sanity checking!
  793.             {
  794.                 FailOSErr(errBinHexMultiSubjectError);
  795.             }
  796.             if (partList->GetEqualItemNo(thisPart))
  797.                 FailOSErr(errBinHexMultiSubjectError); // have it already!
  798.  
  799.             partList->Insert(thisPart);
  800.             while (thisPart > sortedIDList->GetSize())
  801.                 sortedIDList->InsertLast(0); // ugly
  802.             sortedIDList->AtPut(thisPart, id);
  803.         }
  804.  
  805.         // check for missing parts (simple!)
  806.         if (partList->GetSize() < 1) // ups
  807.             FailOSErr(errBinHexMultiSubjectError);
  808.         if (partList->GetSize() < noParts)
  809.             FailOSErr(errBinariesMissingPart);
  810.         if (partList->GetSize() > noParts) // ups
  811.             FailOSErr(errBinHexMultiSubjectError);
  812.         
  813.         // check for bad item-numbers
  814.         for (i = 1; i <= partList->GetSize(); i++)
  815.             if (partList->At(i) != i)
  816.                 FailOSErr(errBinHexMultiSubjectError);
  817.         
  818.         // it passed all the tests, so
  819.         // substitute the old id list with the new
  820.         partList->Free();
  821.         fIDList->Free();
  822.         fIDList = sortedIDList;
  823.         sortedIDList = nil;
  824.         fi.Success();
  825.     }
  826.     else // fail
  827.     {
  828.         FreeIfObject(partList); partList = nil;
  829.         FreeIfObject(sortedIDList); sortedIDList = nil;
  830.         fi.ReSignal();
  831.     }
  832. }
  833.  
  834. void TNewBinCommand::DoPreProcessing()
  835. {
  836.     if (fIDList->GetSize() == 1)
  837.         return; // not processing when only one file
  838.         // missing check for only having part x/y out of a serie!
  839.     FailInfo fi;
  840.     if (fi.Try())
  841.     {
  842. #if qDebugExtractSorting
  843.         fprintf(stderr, "Going to sort %ld articles: ", fIDList->GetSize());
  844.         short i;
  845.         for (i = 1; i <= fIDList->GetSize(); i++)
  846.             fprintf(stderr, "%ld, ", fIDList->At(i));
  847.         fprintf(stderr, "\n");
  848. #endif
  849.         DoTheSorting();
  850. #if qDebugExtractSorting
  851.         fprintf(stderr, "Result of sorting %ld articles: ", fIDList->GetSize());
  852.         for (i = 1; i <= fIDList->GetSize(); i++)
  853.             fprintf(stderr, "%ld, ", fIDList->At(i));
  854.         fprintf(stderr, "\n");
  855. #endif
  856.         fi.Success();
  857.     }
  858.     else
  859.         fi.ReSignal();
  860. }
  861.  
  862. void TNewBinCommand::SetupProgressTexts()
  863. {
  864.     gCurProgress->SetTitle(kExtractProgressTitle);
  865.     gCurProgress->SetText("");
  866. }
  867.  
  868.  
  869. void TNewBinCommand::DoPostProcessing()
  870. {
  871.     if (fSecretDecoderRing)
  872.         fSecretDecoderRing->PostProcess();
  873. #if 1
  874.     if (!fExtractorNeeded)
  875.         return;
  876.     if (!gPrefs->GetBooleanPrefs('BLau'))
  877.         return;
  878.     if (!gPrefs->PrefExists('BLid'))
  879.         FailOSErr(errNoExtractor);
  880.     AliasHandle aliasH = nil;
  881.     VOLATILE(aliasH);
  882.     FailInfo fi;
  883.     if (fi.Try())
  884.     {
  885.         FSSpec spec;
  886.         gPrefs->GetAliasPrefs('BLid', spec);
  887.         if (gPrefs->GetBooleanPrefs('BLOP'))
  888.         {
  889.             fFile->GetAlias(aliasH);
  890.             OpenApplicationDocument(spec, aliasH, false);
  891.         }
  892.         else
  893.             LaunchApplication(spec, false);
  894.         fi.Success();
  895.     }
  896.     else // fail
  897.     {
  898.         DisposeIfHandle(Handle(aliasH));
  899.         FailNewMessage(fi.error, fi.message, messageLaunchExtractorFailed);
  900.     }
  901. #endif
  902. }
  903.  
  904. void TNewBinCommand::OpenFile()
  905. {
  906.     // the decoder handles this
  907. }
  908.  
  909. void TNewBinCommand::CloseFile()
  910. {
  911. }
  912.  
  913. void TNewBinCommand::FlushBuffer()
  914. {
  915. #if qDebug
  916.     if (fBytesInBuffer)
  917.         ProgramBreak("Some people placed bytes in this buffer which is out of use for today");
  918. #endif
  919. }
  920. //********************************************************************************
  921.  
  922. void ExtractBinaries(CommandNumber itsCommandNumber, TGroupDoc *doc, TLongintList *idList)
  923. {
  924.     TNewBinCommand *aCommand = new TNewBinCommand();
  925.     aCommand->INewBinCommand(itsCommandNumber, doc, idList);
  926.     gApplWideThreads->ExecuteCommand(aCommand, "Extract of binaries");
  927. }
  928.